﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using MobileSales.Server.Database;
using MobileSales.Common.Logger;

namespace MobileSales.Server.Services
{
    public class EmployeeService : ServiceBase, IEmployeeService
    {
        #region Employee management

        #region GetEmployee

        public Model.Employees.Employee GetEmployee(int employeeId)
        {
            this.ValidateToken();
            Employee dbEmployee = entities.Employees.Where(x => x.EmployeeId.Equals(employeeId)).FirstOrDefault();
            if (dbEmployee != null)
            {
                Model.Employees.Employee employee = new Model.Employees.Employee()
                {
                    City = dbEmployee.City,
                    Contract = GetContract(dbEmployee.ContractId),
                    Id = dbEmployee.EmployeeId,
                    Firstname = dbEmployee.Firstname,
                    Language = dbEmployee.Language,
                    Lastname = dbEmployee.Lastname,
                    LocationLatitude = dbEmployee.LocationLatitude,
                    LocationLongitude = dbEmployee.LocationLongitute,
                    LocationTimestamp = dbEmployee.LocationTimestamp,
                    Login = dbEmployee.Login,
                    Phone = dbEmployee.Phone,
                    Street = dbEmployee.Street,
                    SupervisorId = dbEmployee.SupervisorId,
                    Zipcode = dbEmployee.Zipcode,
                    Email = dbEmployee.Email,
                    Role = GetRole(dbEmployee.RoleId),
                    Params = GetEmployeeParams(dbEmployee.EmployeeId),
                    AssignedClients = GetEmployeeClients(dbEmployee.EmployeeId)
                };
                return employee;
            }
            else
            {
                CLogger.LogDebug("Employee not found (id:{0})", employeeId);
            }
            return null;
        }

        #endregion

        #region StoreEmployee
        public void StoreEmployee(Model.Employees.Employee employee)
        {
            this.ValidateToken();
            Employee dbEmployee = null;
            if (employee.Id != 0)
            {
                dbEmployee = entities.Employees.Where(x => x.EmployeeId.Equals(employee.Id)).FirstOrDefault();
                if (dbEmployee == null)
                {
                    this.Raise("Employee not found");
                }
            }
            else
            {
                dbEmployee = new Employee();
            }
            dbEmployee.ContractId = employee.Contract.Id;
            dbEmployee.City = employee.City;
            dbEmployee.Firstname = employee.Firstname;
            dbEmployee.Language = employee.Language;
            dbEmployee.Lastname = employee.Lastname;
            dbEmployee.LocationLatitude = employee.LocationLatitude;
            dbEmployee.LocationLongitute = employee.LocationLongitude;
            dbEmployee.LocationTimestamp = employee.LocationTimestamp;
            dbEmployee.Phone = employee.Phone;
            dbEmployee.Street = employee.Street;
            dbEmployee.SupervisorId = employee.SupervisorId;
            dbEmployee.Zipcode = employee.Zipcode;
            dbEmployee.RoleId = employee.Role.Id;
            dbEmployee.Email = employee.Email;
            if (employee.Id == 0)
            {
                dbEmployee.Login = employee.Login;
                dbEmployee.Passwd = employee.Password; 
                entities.Employees.AddObject(dbEmployee);
            }
            entities.SaveChanges();

            this.StoreEmployeeClients(dbEmployee.EmployeeId, employee.AssignedClients);
            this.StoreEmployeeParams(dbEmployee.EmployeeId, employee.Params);
            this.NotifyAll(Util.ServiceType.EmployeeService, "GetEmployee", dbEmployee.EmployeeId);
        }
        #endregion

        #region DeleteEmployee
        public void DeleteEmployee(int employeeId)
        {
            this.ValidateToken();
            Employee dbEmployee = entities.Employees.Where(x => x.EmployeeId.Equals(employeeId)).FirstOrDefault();
            if (dbEmployee != null)
            {
                entities.Employees.DeleteObject(dbEmployee);
                entities.SaveChanges();
                this.NotifyAll(Util.ServiceType.EmployeeService, "DeleteEmployee", employeeId);
            }
            else
            {
                this.Raise("Employee not found!");
            }
        }
        #endregion

        #region GetEmployeeList
        public IEnumerable<Model.Employees.Employee> GetEmployeeList()
        {
            this.ValidateToken();
            IEnumerable<Employee> dbEmployees = entities.Employees;
            return this.GetEmployeeList(dbEmployees);
        }

        public IEnumerable<Model.Employees.Employee> GetEmployeeListByContract(int contractId)
        {
            this.ValidateToken();
            IEnumerable<Employee> dbEmployees = entities.Employees.Where(x => x.Contract.Equals(contractId));
            return this.GetEmployeeList(dbEmployees);
        }

        public IEnumerable<Model.Employees.Employee> GetEmployeeListByRole(int roleId)
        {
            this.ValidateToken();
            IEnumerable<Employee> dbEmployees = entities.Employees.Where(x => x.RoleId.Equals(roleId));
            return this.GetEmployeeList(dbEmployees);
        }

        private IEnumerable<Model.Employees.Employee> GetEmployeeList(IEnumerable<Employee> dbEmployees)
        {
            if (dbEmployees != null)
            {
                IList<Model.Employees.Employee> employees = new List<Model.Employees.Employee>();
                foreach (var dbEmployee in dbEmployees)
                {
                    Model.Employees.Employee employee = new Model.Employees.Employee()
                    {
                        City = dbEmployee.City,
                        Contract = GetContract(dbEmployee.ContractId),
                        Id = dbEmployee.EmployeeId,
                        Firstname = dbEmployee.Firstname,
                        Language = dbEmployee.Language,
                        Lastname = dbEmployee.Lastname,
                        LocationLatitude = dbEmployee.LocationLatitude,
                        LocationLongitude = dbEmployee.LocationLongitute,
                        LocationTimestamp = dbEmployee.LocationTimestamp,
                        Login = dbEmployee.Login,
                        Phone = dbEmployee.Phone,
                        Street = dbEmployee.Street,
                        SupervisorId = dbEmployee.SupervisorId,
                        Zipcode = dbEmployee.Zipcode,
                        Email = dbEmployee.Email,
                        Role = GetRole(dbEmployee.RoleId),
                        Params = GetEmployeeParams(dbEmployee.EmployeeId),
                        AssignedClients = GetEmployeeClients(dbEmployee.EmployeeId)
                    };
                    employees.Add(employee);
                }
                return employees;
            }
            else
            {
                CLogger.LogDebug("Could not get employee list");
            }
            return null;
        }
        #endregion

        #region GetEmployeeLoginList
        public IDictionary<string, int> GetEmployeeLoginList()
        {
            this.ValidateToken();
            IEnumerable<Employee> dbEmployees = entities.Employees;
            if (dbEmployees != null)
            {
                Dictionary<string, int> dict = new Dictionary<string, int>();
                dbEmployees = dbEmployees.OrderBy(x => x.Lastname);
                foreach (var dbEmployee in dbEmployees)
                {
                    dict.Add(String.Format("{0} {1}", dbEmployee.Lastname, dbEmployee.Firstname), dbEmployee.EmployeeId);
                }
                return dict;
            }
            else
                CLogger.LogDebug("Could not get employee login list");
            return null;
        } 
        #endregion

        #region GetEmployeeParams
        public Dictionary<Model.Params.Param, string> GetEmployeeParams(int employeeId)
        {
            IEnumerable<EmployeeHasParam> dbEmployeeHasParams = entities.EmployeeHasParams.Where(x => x.EmployeeId.Equals(employeeId));
            if (dbEmployeeHasParams != null)
            {
                Dictionary<Model.Params.Param, string> dict = new Dictionary<Model.Params.Param, string>();
                foreach (var dbEmployeeHasParam in dbEmployeeHasParams)
                {
                    Model.Params.Param param = this.GetParam(dbEmployeeHasParam.ParamId);
                    if (param != null)
                    {
                        dict.Add(param, dbEmployeeHasParam.Value);
                    }
                    else
                        this.Raise("An error occured during getting some parameters");
                }
                return dict;
            }
            else
            {
                CLogger.LogDebug("Could not get parameters for employee (id:{0})", employeeId);
            }
            return null;
        }
        #endregion

        #region StoreEmployeeParams
        public void StoreEmployeeParams(int employeeId, Dictionary<Model.Params.Param, string> paramDict)
        {
            Employee dbEmployee = entities.Employees.Where(x => x.EmployeeId.Equals(employeeId)).FirstOrDefault();
            if (dbEmployee == null)
            {
                this.Raise("Update failed! Cannot store employee params");
            }

            IEnumerable<EmployeeHasParam> dbEmployeeParamList = dbEmployee.EmployeeHasParams;
            foreach (var param in paramDict.Keys)
            {
                EmployeeHasParam dbEmployeeParam = dbEmployeeParamList.Where(x => x.ParamId.Equals(param.Id)).FirstOrDefault();
                bool exist = true;
                if (dbEmployeeParam == null)
                {
                    exist = false;
                    dbEmployeeParam = new EmployeeHasParam();
                }

                dbEmployeeParam.ParamId = param.Id;
                dbEmployeeParam.EmployeeId = employeeId;
                if (paramDict[param] == null) continue;
                dbEmployeeParam.Value = paramDict[param].ToString();

                if (!exist)
                {
                    dbEmployee.EmployeeHasParams.Add(dbEmployeeParam);
                }
            }
            entities.SaveChanges();

            dbEmployeeParamList = dbEmployee.EmployeeHasParams;
            List<EmployeeHasParam> toRemove = new List<EmployeeHasParam>();
            foreach (var dbEmployeeParam in dbEmployeeParamList)
            {
                if (!paramDict.Keys.Select(x => x.Id).Contains(dbEmployeeParam.ParamId))
                {
                    toRemove.Add(dbEmployeeParam);
                }
            }
            foreach (var dbEmployeeParam in toRemove)
            {
                dbEmployee.EmployeeHasParams.Remove(dbEmployeeParam);
            }
            entities.SaveChanges();
        }
        #endregion

        #region GetEmployeeClients
        public IDictionary<int, bool> GetEmployeeClients(int employeeId)
        {
            this.ValidateToken();
            IEnumerable<EmployeeHasClient> dbEmployeeHasClients = entities.EmployeeHasClients.Where(x => x.EmployeeId.Equals(employeeId));
            if (dbEmployeeHasClients != null)
            {
                IDictionary<int, bool> dict = new Dictionary<int, bool>();
                foreach (var dbEmployeeHasClient in dbEmployeeHasClients)
                {
                    dict.Add(dbEmployeeHasClient.ClientId, dbEmployeeHasClient.LeadEmployee);
                }
                return dict;
            }
            else
            {
                CLogger.LogDebug("Could not get employee client list");
            }
            return null;
        }
        #endregion

        #region StoreEmployeeClients
        public void StoreEmployeeClients(int employeeId, IDictionary<int, bool> employeeClients)
        {
            this.ValidateToken();
            if (employeeClients == null) return;
            if (employeeClients.Count() == 0) return;

            Employee dbEmployee = entities.Employees.Where(x => x.EmployeeId.Equals(employeeId)).FirstOrDefault();
            if (dbEmployee == null)
            {
                this.Raise("Update failed! Cannot store product categories");
            }

            var employeeClientIds = dbEmployee.EmployeeHasClients.Select(y => y.ClientId);
            IEnumerable<int> clientIds = employeeClients.Keys.ToList();

            IEnumerable<EmployeeHasClient> addList = clientIds.Where(x => !employeeClientIds.Contains(x))
                                                              .Select(x => new EmployeeHasClient
                                                              {
                                                                  ClientId = x,
                                                                  EmployeeId = employeeId,
                                                                  LeadEmployee = employeeClients[x]
                                                              });

            IEnumerable<EmployeeHasClient> removeList = dbEmployee.EmployeeHasClients.Where(x => !clientIds.Contains(x.ClientId));
            foreach (EmployeeHasClient clnt in addList)
            {
                dbEmployee.EmployeeHasClients.Add(clnt);
            }
            List<EmployeeHasClient> toRemove = new List<EmployeeHasClient>();
            foreach (EmployeeHasClient clnt in removeList)
            {
                toRemove.Add(clnt);
            }
            foreach (EmployeeHasClient clnt in toRemove)
            {
                dbEmployee.EmployeeHasClients.Remove(clnt);
            }
            entities.SaveChanges();
        }
        #endregion

        #region UpdateEmployeeLocation
        public void UpdateEmployeeLocation(int employeeId, double latitude, double longitude)
        {
            this.ValidateTokenFor(employeeId);
            Employee dbEmployee = entities.Employees.Where(x => x.EmployeeId.Equals(employeeId)).FirstOrDefault();
            if (dbEmployee != null)
            {
                dbEmployee.LocationLatitude = latitude;
                dbEmployee.LocationLongitute = longitude;
                dbEmployee.LocationTimestamp = DateTime.Now;
                entities.SaveChanges();
            }
            else
            {
                this.Raise(string.Format("Location update failed! id:{0} lat:{1} lon:{2}", employeeId, latitude, longitude));
            }
        }
        #endregion 

        #endregion

        #region Message management

        #region GetMessage
        public Model.Employees.Message GetMessage(int messageId)
        {
            Message dbMessage = entities.Messages.Where(x => x.MessageId.Equals(messageId)).FirstOrDefault();
            if (dbMessage != null)
            {
                this.ValidateTokenFor(dbMessage.ReceiverId);
                dbMessage.ReceiveDate = DateTime.Now;
                entities.SaveChanges();
                Model.Employees.Message msg = new Model.Employees.Message()
                {
                    Id = dbMessage.MessageId,
                    Content = dbMessage.Content,
                    IsNew = dbMessage.IsNew,
                    ReceiveDate = dbMessage.ReceiveDate,
                    RecepientId = dbMessage.ReceiverId,
                    RecepientLogin = dbMessage.Recipient.Login,
                    SendDate = dbMessage.SendDate,
                    SenderId = dbMessage.SenderId,
                    SenderLogin = dbMessage.Sender.Login,
                    Subject = dbMessage.Subject
                };
                return msg;
            }
            else
            {
                CLogger.LogDebug("Message not found (id:{0})", messageId);
            }
            return null;
        } 
        #endregion

        #region ReadMessage
        public void ReadMessage(int messageId)
        {
            Message dbMessage = entities.Messages.Where(x => x.MessageId.Equals(messageId)).FirstOrDefault();
            if (dbMessage != null)
            {
                this.ValidateTokenFor(dbMessage.ReceiverId);
                dbMessage.ReceiveDate = DateTime.Now;
                dbMessage.IsNew = false;
                entities.SaveChanges();
            }
        } 
        #endregion

        #region DeleteMessage
        public void DeleteMessage(int messageId)
        {
            Message dbMessage = entities.Messages.Where(x => x.MessageId.Equals(messageId)).FirstOrDefault();
            if (dbMessage != null)
            {
                int recipientId = dbMessage.ReceiverId;
                this.ValidateTokenFor(dbMessage.ReceiverId);
                entities.Messages.DeleteObject(dbMessage);
                entities.SaveChanges();
                this.Notify(recipientId, Util.ServiceType.EmployeeService, "DeleteMessage", messageId);
            }
            else
                this.Raise("Message not found");
        } 
        #endregion

        #region GetMessageList
        public IEnumerable<Model.Employees.Message> GetMessageList(int employeeId)
        {
            this.ValidateTokenFor(employeeId);
            IEnumerable<Message> dbMessages = entities.Messages.Where(x => x.ReceiverId.Equals(employeeId)).OrderByDescending(x => x.SendDate);
            if (dbMessages != null)
            {
                IList<Model.Employees.Message> messages = new List<Model.Employees.Message>();
                foreach (var dbMessage in dbMessages)
                {
                    Model.Employees.Message msg = new Model.Employees.Message()
                    {
                        Id = dbMessage.MessageId,
                        Content = dbMessage.Content,
                        IsNew = dbMessage.IsNew,
                        ReceiveDate = dbMessage.ReceiveDate,
                        RecepientId = dbMessage.ReceiverId,
                        RecepientLogin = dbMessage.Recipient.Login,
                        SendDate = dbMessage.SendDate,
                        SenderId = dbMessage.SenderId,
                        SenderLogin = dbMessage.Sender.Login,
                        Subject = dbMessage.Subject
                    };
                    messages.Add(msg);
                }
                return messages;
            }
            else
            {
                CLogger.LogDebug("Could not get message list");
            }
            return null;
        } 
        #endregion

        #region CountNewMessages
        public int CountNewMessages(int employeeId)
        {
            this.ValidateTokenFor(employeeId);
            return entities.Messages.Count(x => x.ReceiverId.Equals(employeeId) && x.IsNew);
        } 
        #endregion

        #region SendMessage
        public void SendMessage(int senderId, int recipientId, string subject, string content)
        {
            this.ValidateTokenFor(senderId);
            Message dbMessage = new Message()
            {
                Content = content,
                IsNew = true,
                ReceiverId = recipientId,
                SenderId = senderId,
                Subject = subject,
                SendDate = DateTime.Now
            };
            entities.Messages.AddObject(dbMessage);
            entities.SaveChanges();
            this.Notify(recipientId, Util.ServiceType.EmployeeService, "GetMessage", dbMessage.MessageId);
        }  
        #endregion

        #endregion

        #region Role management

        #region GetRoleList
        public IEnumerable<Model.Employees.Role> GetRoleList()
        {
            this.ValidateToken();
            IEnumerable<Role> dbRoles = entities.Roles;
            if (dbRoles != null)
            {
                List<Model.Employees.Role> list = new List<Model.Employees.Role>();
                foreach (var dbRole in dbRoles)
                {
                    Model.Employees.Role role = new Model.Employees.Role
                    {
                        Id = dbRole.RoleId,
                        Name = dbRole.Name,
                        Description = dbRole.Description,
                        HasWebsiteAccess = dbRole.HasWebsiteAccess
                    };
                    list.Add(role);
                }
                return list;
            }
            else
                CLogger.LogDebug("Could not get role list");
            return null;
        } 
        #endregion

        #region GetRole
        public Model.Employees.Role GetRole(int roleId)
        {
            this.ValidateToken();
            Role dbRole = entities.Roles.Where(x => x.RoleId.Equals(roleId)).FirstOrDefault();
            if (dbRole != null)
            {
                Model.Employees.Role role = new Model.Employees.Role
                {
                    Id = dbRole.RoleId,
                    Name = dbRole.Name,
                    Description = dbRole.Description,
                    HasWebsiteAccess = dbRole.HasWebsiteAccess
                };
                return role;
            }
            else
                CLogger.LogDebug("Could not get role (id:{0})", roleId);
            return null;
        } 
        #endregion

        #region StoreRole
        public void StoreRole(Model.Employees.Role role)
        {
            this.ValidateToken();
            Role dbRole = null;
            if (role.Id != 0)
            {
                dbRole = entities.Roles.Where(x => x.RoleId.Equals(role.Id)).FirstOrDefault();
                if (dbRole == null)
                {
                    this.Raise("Role not found!");
                }
            }
            else
            {
                dbRole = new Role();
            }
            dbRole.Name = role.Name;
            dbRole.Description = role.Description;
            dbRole.HasWebsiteAccess = role.HasWebsiteAccess;
            if (role.Id == 0)
            {
                entities.Roles.AddObject(dbRole);
            }
            entities.SaveChanges();
        } 
        #endregion

        #region DeleteRole
        public void DeleteRole(int roleId)
        {
            this.ValidateToken();
            Role dbRole = entities.Roles.Where(x => x.RoleId.Equals(roleId)).FirstOrDefault();
            if (dbRole != null)
            {
                entities.Roles.DeleteObject(dbRole);
                entities.SaveChanges();
            }
            else
            {
                this.Raise("Role not found");
            }
        } 
        #endregion

        #endregion

        #region Contract management

        #region GetContractList
        public IEnumerable<Model.Employees.Contract> GetContractList()
        {
            this.ValidateToken();
            IEnumerable<Contract> dbContracts = entities.Contracts;
            if (dbContracts != null)
            {
                List<Model.Employees.Contract> list = new List<Model.Employees.Contract>();
                foreach (var dbContract in dbContracts)
                {
                    Model.Employees.Contract contract = new Model.Employees.Contract
                    {
                        Id = dbContract.ContractId,
                        Description = dbContract.Description,
                        HourPerDay = dbContract.HourPerDay,
                        Name = dbContract.Name,
                        PricePerHour = dbContract.PricePerHour
                    };
                    list.Add(contract);
                }
                return list;
            }
            else
                CLogger.LogDebug("Could not get contract list");
            return null;
        } 
        #endregion

        #region GetContract
        public Model.Employees.Contract GetContract(int contractId)
        {
            this.ValidateToken();
            Contract dbContract = entities.Contracts.Where(x => x.ContractId.Equals(contractId)).FirstOrDefault();
            if (dbContract != null)
            {
                Model.Employees.Contract contract = new Model.Employees.Contract
                {
                    Id = dbContract.ContractId,
                    Description = dbContract.Description,
                    HourPerDay = dbContract.HourPerDay,
                    Name = dbContract.Name,
                    PricePerHour = dbContract.PricePerHour
                };
                return contract;
            }
            else
                CLogger.LogDebug("Could not get contract (id:{0})", contractId);
            return null;
        } 
        #endregion

        #region StoreContract
        public void StoreContract(Model.Employees.Contract contract)
        {
            this.ValidateToken();
            Contract dbContract = null;
            if (contract.Id != 0)
            {
                dbContract = entities.Contracts.Where(x => x.ContractId.Equals(contract.Id)).FirstOrDefault();
                if (dbContract == null)
                    this.Raise("Contract not found");
            }
            else
            {
                dbContract = new Contract();
            }
            dbContract.Description = contract.Description;
            dbContract.HourPerDay = contract.HourPerDay;
            dbContract.Name = contract.Name;
            dbContract.PricePerHour = contract.PricePerHour;

            if (contract.Id == 0)
                entities.Contracts.AddObject(dbContract);
            entities.SaveChanges();
        } 
        #endregion

        #region DeleteContract
        public void DeleteContract(int contractId)
        {
            this.ValidateToken();
            Contract dbContract = entities.Contracts.Where(x => x.ContractId.Equals(contractId)).FirstOrDefault();
            if (dbContract != null)
            {
                entities.Contracts.DeleteObject(dbContract);
                entities.SaveChanges();
            }
            else
                this.Raise("Contract not found");
        }  
        #endregion

        #endregion
    }
}
